﻿using System.Collections.Generic;
using UnityEngine;

namespace RTR.Transform.VertexBlend
{
    /// <summary>
    /// 骨骼
    /// </summary>
    class Bone
    {
        /// <summary>
        /// 初始骨骼坐标系->世界坐标系
        /// </summary>
        public Matrix4x4 local2WorldMatrix;
        /// <summary>
        /// 初始世界坐标系->骨骼坐标系
        /// </summary>
        public Matrix4x4 world2LocalMatrix;
        /// <summary>
        /// 局部变换矩阵
        /// </summary>
        public Matrix4x4 localMatrix;
        /// <summary>
        /// 父骨骼变换矩阵
        /// </summary>
        public Matrix4x4 parentMatrix;
    }
    
    /// <summary>
    /// Vertex
    /// </summary>
    class Vertex
    {
        /// <summary>
        /// 关联骨骼列表，0位置是主骨骼
        /// </summary>
        public List<Bone> bones;
        /// <summary>
        /// 骨骼相应的权重
        /// </summary>
        public List<float> weights;
        /// <summary>
        /// 初始顶点在主骨骼空间中的位置
        /// </summary>
        public Vector3 localPosition;
        /// <summary>
        /// 初始顶点的世界坐标
        /// </summary>
        public Vector3 worldPosition;
        /// <summary>
        /// 顶点的世界坐标
        /// </summary>
        public Vector3 position;

        public void CalcWorldPosition()
        {
            // 计算初始世界坐标
            worldPosition = bones[0].local2WorldMatrix.MultiplyPoint3x4(localPosition);
        }
    }

    /// <summary>
    /// SkeletonModel
    /// </summary>
    class SkeletonModel
    {
        /// <summary>
        /// 所有骨骼列表
        /// </summary>
        public List<Bone> bones;
        /// <summary>
        /// 所有顶点列表
        /// </summary>
        public List<Vertex> vertices;

        /// <summary>
        /// 计算某个顶点的世界坐标(RTR4: Equation(4.59))
        /// u(t) = \sum_{0}^{n-1}{w * B(i) * M^{-1} * p}
        /// u(t)->变换后的顶点世界坐标
        /// p->初始的顶点的世界坐标
        /// t->时间，w->每根骨骼的权重，B(i)->随时间变化而变化的骨骼当前的骨骼到世界矩阵，M->骨骼到世界矩阵
        /// </summary>
        public Vector3 CalcVertexPosition(int index)
        {
            var vertex = vertices[index];
            var position = Vector3.zero;
            for (var i = 0; i < vertex.bones.Count; ++i)
            {
                var bone = vertex.bones[i];
                var weight = vertex.weights[i];
                position += weight * (bone.parentMatrix * bone.localMatrix * bone.world2LocalMatrix).MultiplyPoint3x4(vertex.worldPosition);
            }

            vertex.position = position;
            return position;
        }
    }
}